from datetime import datetime, timedelta

from . import crud, baseModel
from fastapi import Depends, FastAPI, HTTPException, status
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from jose import JWTError, jwt
from passlib.context import CryptContext
from pydantic import BaseModel

SECRET_KEY = ("c45f1ee2bed8a4e21d31cbb17e120250b6b13e639b60856ff8f407488991c673"
              "7441fe57ae3cb1331afaea4b44a31105ad03cf40c1442b53fea467034ad68d0e")
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30

pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")


def verify_password(plain_password, hashed_password):
    """
    验证明文密码是否与哈希密码相同
    :param plain_password: 明文密码
    :param hashed_password: 哈希密码
    :return:
    """
    return pwd_context.verify(plain_password, hashed_password)


def get_password_hash(password):
    """
    将密码转换为哈希密码
    :param password:密码
    :return: hash密码
    """
    return pwd_context.hash(password)


def authenticate_user(db, account, password):
    """
    验证账号是否正确
    :param db: 数据库
    :param account: 账号
    :param password: 密码
    :return:
    """
    db_user = crud.get_user_by_account(db, account)
    if not db_user:
        return False
    if not verify_password(password, db_user.hashed_password):
        return False
    return db_user


def create_access_token(data: dict, time: int | None = ACCESS_TOKEN_EXPIRE_MINUTES):
    """
    创建用户token
    :param time: 有效时间 min
    :param data: 转换成token的信息
    :return: 返回token
    """
    to_encode = data.copy()
    # 有效时间
    expires_delta = timedelta(minutes=time)
    # 当前时间加上有效时间  = 过期时间
    expire = datetime.utcnow() + expires_delta

    to_encode.update({"exp": expire})
    encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
    return encoded_jwt


def token_analysis(token: str = Depends(oauth2_scheme)):
    """
    验证token
    :param token: token
    :return: token中保存的账号
    """
    credentials_exception = HTTPException(
        status_code=status.HTTP_401_UNAUTHORIZED,
        detail="Token验证失败",
        headers={"WWW-Authenticate": "Bearer"},
    )
    try:
        payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
        account: str = payload.get("sub")
        if account is None:
            raise credentials_exception
        token_data = baseModel.TokenData(account=account)
    except JWTError:
        raise credentials_exception

    return token_data.account
